package com.gome.gop.mybatis.rw.datasource;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import com.gome.gop.mybatis.DynamicDataSources;

public class DynamicDataSourceRegistar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
    
    private ResourceLoader resourceLoader;
    
    private Environment environment;
    
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }
    
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
    
    protected ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }
    
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        
        Set<String> basePackages;
        
        Map<String, Object> attrs = importingClassMetadata.getAnnotationAttributes(DynamicDataSourceRegistar.class.getName());
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(DynamicDataSources.class);
        final Class<?>[] clients = attrs == null ? null : (Class<?>[])attrs.get("clients");
        final Set<String> clientClasses = new HashSet<>();
        basePackages = new HashSet<>();
        if (clients == null || clients.length == 0) {
            scanner.addIncludeFilter(annotationTypeFilter);
            basePackages = getBasePackages(importingClassMetadata);
        }
        else {
            for (Class<?> clazz : clients) {
                basePackages.add(ClassUtils.getPackageName(clazz));
                clientClasses.add(clazz.getCanonicalName());
            }
        }
        if (basePackages != null) {
            for (String basePackage : basePackages) {
                RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
                rootBeanDefinition.setBeanClass(DefaultPointcutAdvisor.class);
                AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
                pointcut.setExpression(new StringBuffer("execution(public * ").append(basePackage).append("..*(..)").append(")").toString());
                rootBeanDefinition.getPropertyValues().add("pointcut", pointcut);
                rootBeanDefinition.getPropertyValues().add("advice", new DynamicDataSourceMethodInterceptor());
                String beanName=new StringBuffer("dynamic-data-source-").append(basePackage.replace(".", "")).toString();
                registry.registerBeanDefinition(beanName, rootBeanDefinition);
            }
        }
       
    }
    
    protected Set<String> getBasePackages(AnnotationMetadata importingClassMetadata) {
        Map<String, Object> attributes = importingClassMetadata.getAnnotationAttributes(DynamicDataSources.class.getCanonicalName());
        
        Set<String> basePackages = new HashSet<>();
        if (!attributes.isEmpty()) {
            
            for (String pkg : (String[])attributes.get("value")) {
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
            for (String pkg : (String[])attributes.get("basePackages")) {
                if (StringUtils.hasText(pkg)) {
                    basePackages.add(pkg);
                }
            }
            for (Class<?> clazz : (Class[])attributes.get("basePackageClasses")) {
                basePackages.add(ClassUtils.getPackageName(clazz));
            }
        }
        if (basePackages.isEmpty()) {
            basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
        }
        return basePackages;
    }
    
}
